package me.edzh.spring.bootstrap.utils.security;

import lombok.var;
import me.edzh.spring.bootstrap.common.GlobalPropertiesConfigurer;
import me.edzh.spring.bootstrap.utils.ObjectMapperFactory;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.io.FileUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.util.Base64Utils;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
import java.util.stream.Collectors;

public class AESUtils {

    public static void main(String[] args) throws Exception {
        //1.构造密钥生成器，指定为AES算法,不区分大小写
//        KeyGenerator keygen = KeyGenerator.getInstance("AES");
//        //2.根据ecnodeRules规则初始化密钥生成器
//        //生成一个128位的随机源,根据传入的字节数组
//        SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
//        random.setSeed("fuckingyhb".getBytes());
//        keygen.init(256, random);
//        //3.产生原始对称密钥
//        SecretKey original_key = keygen.generateKey();
//        toolDecrypt("Le+vtyBRTxC9vk5XuX7+6BZRwdoIBohtqqHjC7mcFpY=");
        toolEncrypt("上海银行");
        toolDecrypt("Gz628u+TrGYvFEMHIg1dnA==");
        toolDecrypt("EzrGCTEt9jNzhZ2Am7PGJQ==");
        //4.获得原始对称密钥的字节数组
//        byte[] raw = original_key.getEncoded();
//        System.out.println(Base64.getEncoder().encodeToString(raw));
//        System.out.println(Hex.encodeHexString(raw));
//
//        //5.根据字节数组生成AES密钥
//        SecretKey key = new SecretKeySpec(raw, "AES");
//        System.out.println("key:" + Base64.getEncoder().encodeToString(key.getEncoded()));
////        //6.根据指定算法AES自成密码器
//        Cipher cipher = Cipher.getInstance("AES");
////        //7.初始化密码器，第一个参数为加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作，第二个参数为使用的KEY
//        cipher.init(Cipher.ENCRYPT_MODE, key);
//        //8.获取加密内容的字节数组(这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
//        byte[] byte_encode = content.getBytes("utf-8");
//        //9.根据密码器的初始化方式--加密：将数据加密
//        byte[] byte_AES = cipher.doFinal(byte_encode);
//        //10.将加密后的数据转换为字符串
//        //这里用Base64Encoder中会找不到包
//        //解决办法：
//        //在项目的Build path中先移除JRE System Library，再添加库JRE System Library，重新编译后就一切正常了。
//        String AES_encode = new String(new BASE64Encoder().encode(byte_AES));
//        //11.将字符串返回
////        return AES_encode;


//
//
//        Key aesKey;
////
//        String keyBase64 = "zmLPmHKLor6SvWqrIXQvhmIWP66l3+wIhhwWElP9VYc=";
//        byte[] raw = Base64.getDecoder().decode(keyBase64);
//        aesKey = new SecretKeySpec(raw, "AES");
////
//        AlgorithmParameterSpec aesIv = new IvParameterSpec("chenjiahandsome!".getBytes());
//        System.out.println(new String(decrypt(aesKey, Base64Utils.decodeFromString("mFwRyOdnfi7fFxXHQh/DvuoNeY/ahfTy8A0uXaZOdLU="), "AES/CBC/PKCS5Padding", aesIv), StandardCharsets.UTF_8));
//        System.out.println(new String(decrypt(aesKey, Base64Utils.decodeFromString("h7K9MUok/lQP6QsjCYXBYzmFjY9qliovTguPISy0aq8="),"AES/CBC/PKCS5Padding", aesIv), StandardCharsets.UTF_8));
//
//        System.out.println(Base64Utils.encodeToString(encrypt(aesKey, "0723".getBytes(StandardCharsets.UTF_8))));
//        System.out.println(Base64Utils.encodeToString(encrypt(aesKey, "998".getBytes(StandardCharsets.UTF_8))));
//        System.out.println(Base64Utils.encodeToString(encrypt(aesKey, "招商银行".getBytes(StandardCharsets.UTF_8))));
////        System.out.println(Base64Utils.encodeToString(encrypt(aesKey, "南京银行".getBytes(StandardCharsets.UTF_8))));
////        System.out.println(Base64Utils.encodeToString(encrypt(aesKey, "南京分行仙林支行".getBytes(StandardCharsets.UTF_8))));
//
//        String[] ori = FileUtils.readFileToString(new File("/Users/edwardzhou/Desktop/transport/ori.txt"), StandardCharsets.UTF_8).split("\n");
//        var sec = Arrays.asList(ori).parallelStream().map(o -> StringUtils.isEmpty(o) ? "NULL" : Base64Utils.encodeToString(encrypt(aesKey, o.getBytes(StandardCharsets.UTF_8), "AES/CBC/PKCS5Padding", aesIv))).collect(Collectors.toList());
//        FileUtils.writeStringToFile(new File("/Users/edwardzhou/Desktop/transport/sec.txt"), String.join("\n", sec), StandardCharsets.UTF_8);
//        var hash = Arrays.asList(ori).parallelStream().map(o -> StringUtils.isEmpty(o) ? "NULL" : "" + StringUtils.getHash(o)).collect(Collectors.toList());
//        FileUtils.writeStringToFile(new File("/Users/edwardzhou/Desktop/transport/hash.txt"), String.join("\n", hash), StandardCharsets.UTF_8);

    }

    public static Key keyFromBase64(String base64) {
        byte[] raw = Base64.getDecoder().decode(base64);
        return new SecretKeySpec(raw, "AES");
    }

    public static byte[] encrypt(Key key, byte[] content) throws Exception {
        return encrypt(key, content, "AES");
    }

    public static byte[] decrypt(Key key, byte[] content) throws Exception {
        return decrypt(key, content, "AES");
    }

    public static byte[] encrypt(Key key, byte[] content, String algorithm) throws Exception {
        Cipher cipher = Cipher.getInstance(algorithm);
        cipher.init(Cipher.ENCRYPT_MODE, key);
        return cipher.doFinal(content);
    }

    public static byte[] encrypt(Key key, byte[] content, String algorithm, AlgorithmParameterSpec algorithmParameterSpec) {

        try {
            Cipher cipher = Cipher.getInstance(algorithm);
            cipher.init(Cipher.ENCRYPT_MODE, key, algorithmParameterSpec);
            return cipher.doFinal(content);

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static byte[] decrypt(Key key, byte[] content, String algorithm) throws Exception {
        Cipher cipher = Cipher.getInstance(algorithm);
        cipher.init(Cipher.DECRYPT_MODE, key);
        return cipher.doFinal(content);
    }

    public static byte[] decrypt(Key key, byte[] content, String algorithm, AlgorithmParameterSpec algorithmParameterSpec) throws Exception {
        Cipher cipher = Cipher.getInstance(algorithm);
        cipher.init(Cipher.DECRYPT_MODE, key, algorithmParameterSpec);
        return cipher.doFinal(content);
    }

    public static void toolDecrypt(String src) throws Exception {
        Key aesKey;
        String keyBase64 = "zmLPmHKLor6SvWqrIXQvhmIWP66l3+wIhhwWElP9VYc=";
        byte[] raw = Base64.getDecoder().decode(keyBase64);
        aesKey = new SecretKeySpec(raw, "AES");
        AlgorithmParameterSpec aesIv = new IvParameterSpec("chenjiahandsome!".getBytes());
        System.out.println(
                new String(decrypt(aesKey, Base64Utils.decodeFromString(src),
                        "AES/CBC/PKCS5Padding", aesIv
                ), StandardCharsets.UTF_8)
        );
    }

    public static void toolEncrypt(@NotNull String src) throws Exception {
        Key aesKey;
        String keyBase64 = "zmLPmHKLor6SvWqrIXQvhmIWP66l3+wIhhwWElP9VYc=";
        byte[] raw = Base64.getDecoder().decode(keyBase64);
        aesKey = new SecretKeySpec(raw, "AES");
        AlgorithmParameterSpec aesIv = new IvParameterSpec("chenjiahandsome!".getBytes());
        var res = encrypt(aesKey,
                src.getBytes(StandardCharsets.UTF_8),
                "AES/CBC/PKCS5Padding",
                aesIv);
        System.out.println(Base64.getEncoder().encodeToString(res));
    }
}
